package com.satis.compose.get.navigator

import android.content.Intent
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.core.net.toUri
import androidx.navigation.*
import com.google.gson.reflect.TypeToken
import com.satis.compose.get.gson
import java.net.URLDecoder
import java.net.URLEncoder

/**
 * @author sunshaobei on 2023/4/2
 */
/**
 * activity 级别导航器缓存
 */
val LocalPageController =
    compositionLocalOf<GetPageController> { error("LocalNavHostController is null") }


@Composable
internal fun rememberGetPageController(
    routeNoFoundDestination: String?, navigator: NavHostController
): GetPageController {
    return remember { GetPageControllerDelegate(navigator, routeNoFoundDestination) }
}


internal class GetPageControllerDelegate(
    val navigator: NavHostController, override val routeNoFoundDestination: String?
) : GetPageController {
    override fun push(
        route: String,
        vararg pairs: Pair<String, Any>,
        builder: (NavOptionsBuilder.() -> Unit)?
    ) {
        var route = route
        if (pairs.isNotEmpty()){
            pairs.forEachIndexed { index, pair ->
                route = if (index == 0){
                    "$route/?"
                }else{
                    "$route&"
                }
                route = route + pair.first+"="+encodeObjToUrl(pair.second)
            }
        }
        val request = NavDeepLinkRequest.Builder.fromUri(NavDestination.createRoute(route).toUri()).build()
        val deepLinkMatch = navigator.graph.matchDeepLink(request)
        if (deepLinkMatch != null) {
            val destination = deepLinkMatch.destination
            val args = destination.addInDefaultArgs(deepLinkMatch.matchingArgs) ?: Bundle()
            val intent = Intent().apply {
                setDataAndType(request.uri, request.mimeType)
                action = request.action
            }
            args.putParcelable(NavController.KEY_DEEP_LINK_INTENT, intent)
            val reflectNavigate = NavController::class.java.getDeclaredMethod(
                "navigate",
                NavDestination::class.java,
                Bundle::class.java,
                NavOptions::class.java,
                Navigator.Extras::class.java
            )
            reflectNavigate.isAccessible = true
            reflectNavigate.invoke(navigator,destination,args,if (builder != null) navOptions(builder) else null, null)
        } else {
            var error = ""
            if (routeNoFoundDestination != route) {
                if (routeNoFoundDestination != null) {
                    push(
                        routeNoFoundDestination,
                        Pair(GetParam.PAGE_NO_FOUND, "未找到对应路由地址:$route")
                    )
                    return
                } else {
                    error = "No Page corresponding to the \"$route\" could be found"
                }

            } else {
                /**
                 *  definedPage(
                route= routeNoFoundDestination,
                enter= PageRoute.Animation.LEFT_IN,
                exit= PageRoute.Animation.LEFT_OUT,
                popEnter= PageRoute.Animation.RIGHT_IN,
                popExit= PageRoute.Animation.RIGHT_OUT) {
                RouteNoFoundPage()
                }
                 */
                error =
                    "You did not correctly define routeNoFoundDestination(\"$routeNoFoundDestination\") on GetApp"
            }
            throw IllegalArgumentException(error)
        }
    }


    private fun encodeObjToUrl(value:Any):String{
        return when (value){
            is String,
            is Int,
            is Long,
            is Boolean->{
                value.toString()
            }
            else ->{
                URLEncoder.encode(gson.toJson(value),"UTF-8")
            }
        }
    }


    override fun back() {
        navigator.popBackStack()
    }

    override fun backTo(destination: String) {
        navigator.popBackStack(destination, inclusive = true, false)
    }

    override fun route(): String {
        return navigator.currentDestination!!.route!!
    }
}

interface GetPageController {
    val routeNoFoundDestination: String?
    fun push(
        route: String,
        vararg pairs: Pair<String, Any>,
        builder: (NavOptionsBuilder.() -> Unit)? = null
    )

    fun back()
    fun backTo(route: String)
    fun route(): String
}

object GetParam {
    const val PAGE_NO_FOUND = "page_no_found"
}

inline fun <reified T> String.decodeFormUrl():T{
    val json = URLDecoder.decode(this,"UTF-8")
    val t = gson.fromJson<T>(json,object : TypeToken<T>() {}.type)
    return t
}